﻿namespace UsageTracker.Reporting.Data
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Reflection;
    using System.Text;

    using log4net;

    using Newtonsoft.Json.Linq;
    using UsageTracker.MongoDb.Processor;
    using UsageTracker.Reporting.Util;


    public class UsageDataAccess
    {
        /// <summary>
        /// 
        /// </summary>
        private readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        
        /// <summary>
        /// 
        /// </summary>
        public string ChartingFolderPath { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="productName"></param>
        /// <param name="hostName"></param>
        public string GetProductUsageDistribution(string productName, DateTime startDate, DateTime endDate, string hostName = null)
        {
            DbProcessor mongoDb = new DbProcessor(productName);
            string chartPath = String.Empty;
            Dictionary<string, Dictionary<string, int>> appDistribution = mongoDb.GetProductModuleUsageDetails(productName, startDate, endDate, hostName);
            if (appDistribution.Count > 0)
            {
                chartPath = this.PlotUsageDistribution(appDistribution, productName);
            }
            return chartPath;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<string> GetAllProductNames()
        {
            DbProcessor mongoDb = new DbProcessor();
            return mongoDb.GetAllCollections();
        }

        /// <summary>
        /// Gets all installations for a specific product. The returned list contains a sub-list of each location's attributes
        /// in the form - Country, City, Latitude, Longitude, HostName
        /// </summary>
        /// <param name="productName"></param>
        /// <returns></returns>
        public List<List<string>> GetAllHostsForProduct(string productName, DateTime startDate, DateTime endDate)
        {
            List<List<string>> ipGeoDetails = new List<List<string>>();
            DbProcessor mongoDb = new DbProcessor(productName);

            //Contains machine name versus public IP address
            Dictionary<string, string> appGeoDistribution = mongoDb.GetProductGeoDistribution(productName, startDate, endDate);

            appGeoDistribution.Keys.ToList().ForEach(
                k =>
                {
                    List<string> geoDetails = this.GetProductLocation(appGeoDistribution[k]);
                    //Add the host name to the data
                    geoDetails.Add(k);
                    ipGeoDetails.Add(geoDetails);
                });

            return ipGeoDetails;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="productName"></param>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <param name="hostName"></param>
        /// <returns></returns>
        public string GetProductInstallationDistribution(
            string productName,
            DateTime startDate,
            DateTime endDate,
            string hostName = null)
        {
            if (!Directory.Exists(this.ChartingFolderPath))
            {
                Directory.CreateDirectory(this.ChartingFolderPath);
            }
            Dictionary<string, int> geoDistribution = new Dictionary<string, int>();
            var locDetails = this.GetAllHostsForProduct(productName, startDate, endDate).GroupBy(l => l[1]).Select(l => l);
            locDetails.ToList().ForEach(
                l =>
                {
                    string country = l.FirstOrDefault()[0];
                    geoDistribution[country + " (" + l.Key + ")"] = l.Count();
                });

            string datePart = DateTime.Now.ToString().Replace(":", "_").Replace(@"/", "_");
            string chartFileName = "InstallDistrib_" + datePart + ".png";
            Util.Utility.DrawUsageDistribution(
                geoDistribution,
                Path.Combine(this.ChartingFolderPath, chartFileName),
                "Install Locations",
                "Count of Installations",
                "Install Location Distribution");
            return chartFileName;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="publicIp"></param>
        /// <returns></returns>
        private List<string> GetProductLocation(string publicIp)
        {
            List<string> response = new List<string>();
            using (WebClient client = new WebClient())
            {
                string JsonResponse = String.Empty;
                client.Encoding = Encoding.UTF8;
                client.Headers.Add(HttpRequestHeader.ContentType, "text/json");
                try
                {
                    JsonResponse = client.DownloadString(new Uri("http://www.telize.com/geoip/" + publicIp));
                    JObject geoData = JObject.Parse(JsonResponse);
                    response.Add(geoData["country"].ToString());
                    response.Add(geoData["city"].ToString());
                    response.Add(geoData["latitude"].ToString());
                    response.Add(geoData["longitude"].ToString());
                }
                catch (Exception exp)
                {
#if DEBUG
                    Debug.Print("* Usage Logger Error * - Could not fetch the external/public IP for the current network environment. " + exp.Message);
#endif
                }
            }
            return response;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="appDistribution"></param>
        private string PlotUsageDistribution(Dictionary<string, Dictionary<string, int>> appDistribution, string productName)
        {
            if (!Directory.Exists(this.ChartingFolderPath))
            {
                Directory.CreateDirectory(this.ChartingFolderPath);
                log.Info("Created charts folder at: " + this.ChartingFolderPath);
            }

            Dictionary<string, int> consolidatedUsage = new Dictionary<string, int>();
            appDistribution.Keys.ToList().ForEach(
                a =>
                {
                    var appUsageLocal = appDistribution[a];
                    appUsageLocal.Keys.ToList().ForEach(
                        k =>
                        {
                            if (!consolidatedUsage.ContainsKey(k))
                            {
                                consolidatedUsage.Add(k, appUsageLocal[k]);
                            }
                            else
                            {
                                consolidatedUsage[k] += appUsageLocal[k];
                            }
                        });
                });
            string datePart = DateTime.Now.ToString().Replace(":", "_").Replace(@"/", "_");
            string chartFileName = "UsageDistrib_" + datePart + ".png";
            Utility.DrawUsageDistribution(consolidatedUsage, Path.Combine(this.ChartingFolderPath, chartFileName), "Module Name", "Count of Invocations", "Module Usage Distribution for " + productName);
            log.Info("Created a new chart at: " + Path.Combine(this.ChartingFolderPath, chartFileName));
            return chartFileName;
        }
    }
}